const util = require('util')

const jwt = require('jsonwebtoken')

const short = require('short-uuid')
const translator = short()

const WeixinAuth = require('../utils/koa2-weixin-auth')
const WXBizDataCrypt = require('../utils/WXBizDataCrypt')

const userModel = require('../models/user.model')
const sessionKeyModel = require('../models/sessionKey.model')
const cartModel = require('../models/cart.model')
const goodsSkuModel = require('../models/goods-sku.model')
const addressModel = require('../models/address.model')
const orderModel = require('../models/order.model')
const goodsModel = require('../models/goods.model')

const { PRIVATE_KEY, PUBLIC_KEY, APP_ID, APP_SECRET } = require('../app/config')

// 小程序的重要信息
// WeixinAuth 内部封装有处理 token 的相关方法
const weixinAuth = new WeixinAuth(APP_ID, APP_SECRET)

class UserController {
	async wxLogin(ctx, next) {
		// console.log('进入wxLogin----')
		console.log('request.body---', ctx.request.body)
		// code 可以通过在前端调用 wx.login 获得
		// userInfo,encryptedData,iv可以在前端调用 wx.getUserInfo 获得（配合button的open-type属性）
		let {
			code,
			userInfo,
			encryptedData,
			iv,
			sessionKeyIsValid,
		} = ctx.request.body

		console.log('sessionKeyIsValid', sessionKeyIsValid)

		let sessionKey
		let sessionKeyRecordId
		// 如果 session_key 仍然有效，则复用 token
		// 如果客户端有token，则传来，解析
		if (sessionKeyIsValid) {
			console.log('ctx.request.header---', ctx.request.header)
			let token = ctx.request.header.authorization
			token = token.split(' ')[1]
			// 如果 sessonKey 和 token 均存在，则说明2者都未过期，可继续使用
			if (token) {
				let payload = await util
					.promisify(jsonwebtoken.verify)(token, PUBLIC_KEY, {
						algorithms: ['RS256'],
					})
					.catch((err) => {
						console.log('err', err)
					})
				// 关于 payload 具体包含的内容如下jsonwebtoken.sign加密的内容
				console.log('payload', payload)
				// payload有可能不存在（token失效，比如token过期或者token被人篡改或者token被人清空等等都会导致payload不存在）
				if (payload) {
					sessionKey = payload.sessionKey
					sessionKeyRecordId = payload.sessionKeyRecordId
				}
			}
		}

		// 【sessionKey的持久化】
		// 除了尝试从token中获取sessionKey，还可以从数据库中或服务器redis缓存中获取
		// 如果在db或redis中存储
		console.log('login2 sessionKey---', sessionKey)
		// 如果没有找到历史上有效的sessionKey，从db中取一下
		if (sessionKeyIsValid && !sessionKey && sessionKeyRecordId) {
			// 因为这里使用的是 jwt 机制，不使用 cookie和session 技术，所以，关于本项目中的ctx.session.sessionKeyRecordId均为undefined
			// 解决方案：将 sessionKeyRecordId 也一并放置 payload 中！
			// 不使用 sequelize 的写法
			// const [sesskonKeyRecordOld] = await sessionKeyModel.getSessionKeyRecordById(ctx.session.sessionKeyRecordId)
			const [
				sesskonKeyRecordOld,
			] = await sessionKeyModel.getSessionKeyRecordById(sessionKeyRecordId)

			if (sesskonKeyRecordOld) sessionKey = sesskonKeyRecordOld.sessionKey
			console.log('从db中查找sessionKey---', sessionKey)
		}

		// 如果从token或者db中都没有取到session_key，则从微信服务器上再取一次
		if (!sessionKey) {
			console.log('进来getAccessToken ---')
			try {
				const token = await weixinAuth.getAccessToken(code)
				// 目前微信的 session_key, 有效期3天
				sessionKey = token.data.session_key
				console.log('从微信服务器上拿下来的session_key---', sessionKey)
			} catch (error) {
				console.log('getAccessToken error---', error)
			}
		}

		let decryptedUserInfo
		var pc = new WXBizDataCrypt(APP_ID, sessionKey)

		// 有可能因为sessionKey不与code匹配，而出错
		// 通过错误，通知前端再重新拉取code（所以前端允许3次重试）
		decryptedUserInfo = pc.decryptData(encryptedData, iv)
		console.log('decryptedUserInfo---', decryptedUserInfo)

		// 第一次对用户进行查找时，必须使用 openId（业务需要）
		let [user] = await userModel.getUserByOpenId(decryptedUserInfo.openId)
		if (!user) {
			//如果用户没有查到（即为 undefined），则创建对应用户

			// 当创建了用户后，可以通过 openId 或者 id 进行查找。这里选择通过 id 查找
			let createRes = await userModel.createUser(decryptedUserInfo)
			if (createRes) {
				;[user] = await userModel.getUserById(createRes.insertId)
			}
		}

		console.log('user.id---', user.id)
		let [sessionKeyRecord] = await sessionKeyModel.getSessionKeyRecordByUid(
			user.id
		)
		if (sessionKeyRecord) {
			await sessionKeyModel.updateSessionKey(sessionKey, user.id)
		} else {
			let sessionKeyRecordCreateRes = await sessionKeyModel.createSessionKeyRecord(
				sessionKey,
				user.id
			)
			sessionKeyRecord = {}
			sessionKeyRecord.id = sessionKeyRecordCreateRes.insertId
			// console.log("created record", sessionKeyRecord);
		}

		console.log('sessionKeyRecordId----', sessionKeyRecord.id)

		// 添加上openId与sessionKey与sessionKeyRecordId
		let authorizationToken = jwt.sign(
			{
				uid: user.id,
				nickName: decryptedUserInfo.nickName,
				avatarUrl: decryptedUserInfo.avatarUrl,
				openId: decryptedUserInfo.openId,
				sessionKey: sessionKey,
				sessionKeyRecordId: sessionKeyRecord.id, // sessionKeyRecordId一并放到 token 中
			},
			PRIVATE_KEY,
			{ expiresIn: '3d', algorithm: 'RS256' } //修改为3天，这是sessionKey的有效时间
		)

		Object.assign(decryptedUserInfo, { authorizationToken })

		ctx.body = {
			returnCode: 200,
			data: decryptedUserInfo,
		}
	}

	async testHome(ctx, next) {
		console.log('进来了testHome')
		let name = ctx.request.query.name || ''
		// 取出并打印user对象（ctx.user的设置见上边对应的内容）
		let user = ctx.user
		// console.log('user---', user)
		ctx.status = 200
		ctx.body = {
			code: 200,
			msg: `ok，${name}`,
		}
	}

	async getUserInfo(ctx, next) {
		const { uid, nickName, avatarUrl } = ctx.user

		ctx.body = {
			returnCode: 200,
			data: {
				uid,
				nickName,
				avatarUrl,
			},
		}
	}

	async getCartList(ctx, next) {
		console.log('getCartList---')
		const { uid } = ctx.user

		const result = await cartModel.getCartListByUid(uid)

		ctx.body = {
			returnCode: 200,
			data: result.length ? result : [],
		}
	}

	async addToCart(ctx, next) {
		const { uid } = ctx.user
		let { goodsId, goodsSkuId, goodsSkuDesc, num } = ctx.request.body

		console.log('ctx.request.body---', ctx.request.body)

		goodsId = Number(goodsId)

		// console.log(
		// 	'uid, goodsId, goodsSkuId, goodsSkuDesc, num---',
		// 	uid,
		// 	goodsId,
		// 	goodsSkuId,
		// 	goodsSkuDesc,
		// 	num
		// )

		try {
			// 获取购物车商品数据
			const cartGoodsObj = await cartModel.getCartGoods(
				uid,
				goodsId,
				goodsSkuId
			)
			if (cartGoodsObj.length === 0) {
				// 购物车中未存在相同规格的商品，则向购物车数据表中添加数据
				await cartModel.createCartGoodsRecord(
					uid,
					goodsId,
					goodsSkuId,
					goodsSkuDesc,
					num
				)
			} else {
				// 如果添加的是相同规格（包括尺码和颜色）的同一个商品，则更新商品购买数量
				const numRes = cartGoodsObj[0].num

				// 具体规格信息的商品的库存是有限的，不能在购物车中显示超过库存数的购买量
				const [goodsSkuObj] = await goodsSkuModel.getStockByGoodsSkuId(
					goodsSkuId
				)
				const stock = goodsSkuObj.stock
				const goodsSkuStock = stock
				const totalNum = numRes + num

				if (totalNum >= goodsSkuStock) {
					await cartModel.updateCartGoodsNum(
						goodsSkuStock,
						uid,
						goodsId,
						goodsSkuId
					)
				} else {
					await cartModel.updateCartGoodsNum(totalNum, uid, goodsId, goodsSkuId)
				}
			}

			ctx.body = {
				returnCode: 200,
				message: '加入购物车成功',
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async modifyCartGoodsNum(ctx, next) {
		const { cartId } = ctx.request.params
		console.log('ctx.request.body---', ctx.request.body)
		const { operator, num } = ctx.request.body

		try {
			const result = await cartModel.getCartGoodsByCartId(cartId)
			const numRes = result[0].num
			const stockRes = result[0].stock
			console.log('result[0]---', result[0])

			if (operator === '-') {
				let ret1 = numRes - num
				// 购物车商品显示数量和数据表中存储的数量最少为1
				if (ret1 < 1) {
					ret1 = 1
				}
				await cartModel.modifyCartGoodsNumByCartId(ret1, cartId)
			} else if (operator === '+') {
				let ret2 = numRes + num
				// 购物车显示的商品数量最多为对应规格商品的库存量
				if (ret2 > stockRes) {
					ret2 = stockRes
				}
				await cartModel.modifyCartGoodsNumByCartId(ret2, cartId)
			} else {
				let ret3 = num
				if (ret3 <= 1) {
					ret3 = 1
				} else if (ret3 >= stockRes) {
					ret3 = stockRes
				}
				await cartModel.modifyCartGoodsNumByCartId(ret3, cartId)
			}

			const updatedResult = await cartModel.getCartGoodsByCartId(cartId)

			ctx.body = {
				returnCode: 200,
				message: '更改购买数量成功~',
				data: updatedResult[0],
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async removeCartGoods(ctx, next) {
		const { cartId } = ctx.request.params

		try {
			await cartModel.deleteCartGoodsByCartId(cartId)

			ctx.body = {
				returnCode: 200,
				message: '删除购物车商品成功~',
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async removeAllCartGoods(ctx, next) {
		const { uid } = ctx.user

		try {
			await cartModel.deleteAllCartGoods(uid)

			ctx.body = {
				returnCode: 200,
				message: '删除购物车所有商品成功~',
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async getAddressList(ctx, next) {
		const { uid } = ctx.user

		const result = await addressModel.getAddressListByUid(uid)

		ctx.body = {
			returnCode: 200,
			data: result.length ? result : [],
		}
	}

	async createAddress(ctx, next) {
		const { uid } = ctx.user
		const { userName, telNumber, region, detailInfo } = ctx.request.body

		try {
			// 检查是否存在相同的地址数据
			const result1 = await addressModel.getAddressInfo(
				uid,
				userName.trim(),
				telNumber,
				region.trim(),
				detailInfo.trim()
			)
			console.log('hasExistedAddress---', result1)

			if (result1.length === 0) {
				const { insertId } = await addressModel.createAddress(
					uid,
					userName,
					telNumber,
					region,
					detailInfo
				)

				// 根据 insertId 把数据库中的地址对象返回给前端
				const result2 = await addressModel.getAddressByid(insertId)

				ctx.body = {
					returnCode: 200,
					message: '用户地址创建成功~',
					addressInfo: result2[0],
				}
			} else {
				ctx.body = {
					returnCode: 60002,
					message: '用户地址已存在~',
					addressInfo: result1[0],
				}
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async updateAddress(ctx, next) {
		const { addressId } = ctx.request.params
		const { userName, telNumber, region, detailInfo } = ctx.request.body

		try {
			await addressModel.updateAddressById(
				userName,
				telNumber,
				region,
				detailInfo,
				addressId
			)

			ctx.body = {
				returnCode: 200,
				message: '用户地址修改成功~',
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async removeAddress(ctx, next) {
		const { addressId } = ctx.request.params

		try {
			await addressModel.deleteAddressById(addressId)

			ctx.body = {
				returnCode: 200,
				message: '删除地址成功~',
			}
		} catch (error) {
			console.log('error---', error)
		}
	}

	async generateOrder(ctx, next) {
		const { uid } = ctx.user
		console.log('ctx.request.body---', ctx.request.body)
		const {
			totalFee,
			addressId,
			preOrderGoodsList,
			goodsNameDesc,
			leaveMessage,
		} = ctx.request.body

		// 订单编号（模拟）
		const orderNo = `DG${Date.now()}${Math.random()
			.toString()
			.replace('.', '')
			.substr(0, 8)}`
		// 微信商家单号
		const outTradeNo = `${new Date().getFullYear()}${translator.new()}`

		const payState = 0 // 表示“未支付”

		const goodsCartIds = preOrderGoodsList.map((item) => item.cartId)
		const goodsIds = preOrderGoodsList.map((item) => item.goodsId)
		const goodsNums = preOrderGoodsList.map((item) => item.num)
		const goodsSkuDescs = preOrderGoodsList.map((item) => item.goodsSkuDesc)

		try {
			// 先对商品库存进行检测
			for (let i = 0; i < preOrderGoodsList.length; i++) {
				const item = preOrderGoodsList[i]
				const [goodsSkuObj] = await goodsSkuModel.getStockByGoodsSkuId(
					item.goodsSkuId
				)
				const stock = goodsSkuObj.stock
				// 说明库存不足
				if (stock < item.num) {
					ctx.body = {
						returnCode: 200,
						message: '商品库存不足',
					}

					return
				}

				// 如果库存足够，则更新商品库存：先拿到库存，然后对库存进行修改，最后再更新库存
				await goodsSkuModel.updateStockByGoodsSkuId(
					item.goodsSkuId,
					stock - item.num
				)
			}

			// 如果商品的库存足够，则生成订单
			const orderObj = await orderModel.createOrder(
				uid,
				outTradeNo,
				orderNo,
				payState,
				totalFee,
				addressId,
				goodsCartIds,
				goodsIds,
				goodsNums,
				goodsSkuDescs,
				goodsNameDesc,
				leaveMessage
			)

			const [orderRecord] = await orderModel.getOrderRecordByOrderId(
				orderObj.insertId
			)

			ctx.body = {
				returnCode: 200,
				message: '生成订单成功',
				data: orderRecord,
			}
		} catch (error) {
			console.log('generateOrder error---', error)
		}
	}

	async updateOrderPayState(ctx, next) {
		const { uid } = ctx.user
		const { orderNo } = ctx.request.query
		const { payState } = ctx.request.body

		await orderModel.updateOrderPayStateByOrderNo(uid, orderNo, payState)

		ctx.body = {
			returnCode: 200,
			message: payState === 1 ? '支付成功' : '支付失败',
		}
	}

	async getOrderList(ctx, next) {
		const { uid } = ctx.user

		try {
			// await 不能放在 forEach！
			const orderList = await orderModel.getOrderList(uid)

			for (let i = 0; i < orderList.length; i++) {
				const goodsInfos = []
				const order = orderList[i]
				for (let j = 0; j < order.goodsIds.length; j++) {
					const goodsId = order.goodsIds[j]
					const [result] = await goodsModel.getGoodsInfo(goodsId)
					goodsInfos.push(result)
				}

				order.goodsInfos = goodsInfos
				delete order.goodsIds // 删除goodsIds，因为转换成了goodsInfos，避免数据冗余
			}

			ctx.body = {
				returnCode: 200,
				data: orderList,
			}
		} catch (error) {
			console.log('getOrderList err----', error)
		}
	}
}

module.exports = new UserController()
